package zhoug.chart.utils;

import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.text.TextUtils;
import android.util.SparseIntArray;

import com.github.mikephil.charting.charts.Chart;
import com.github.mikephil.charting.data.ChartData;
import com.github.mikephil.charting.data.CombinedData;
import com.github.mikephil.charting.data.Entry;
import com.github.mikephil.charting.interfaces.datasets.IBarDataSet;
import com.github.mikephil.charting.interfaces.datasets.IBarLineScatterCandleBubbleDataSet;
import com.github.mikephil.charting.interfaces.datasets.ICandleDataSet;
import com.github.mikephil.charting.interfaces.datasets.IDataSet;
import com.github.mikephil.charting.interfaces.datasets.ILineDataSet;
import com.github.mikephil.charting.interfaces.datasets.IPieDataSet;
import com.github.mikephil.charting.model.GradientColor;
import com.github.mikephil.charting.utils.ColorTemplate;
import zhoug.chart.Chart2Sdk;
import zhoug.chart.core.MPConstant;
import zhoug.chart.data.MPLegendEntry;
import zhoug.chart.views.MPIconView;
import zhoug.logging.Logger;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;

import androidx.annotation.ColorInt;
import androidx.annotation.FloatRange;
import androidx.annotation.NonNull;

/**
 * @Author: zhoug
 * @Date: 2024-01-30
 * @Description:
 */
public class MPChartUtils {
    private static final String TAG = ">>>ChartUtils";
    private static boolean DEBUG = Chart2Sdk.isDebug();

    /**
     * 颜色转化成16进制字符串 argb
     *
     * @param color
     * @return
     */
    public static String colorToHex(int color) {
        String s = Integer.toHexString(color);
        int zero = 8 - s.length();
        for (int i = 0; i < zero; i++) {
            s = "0" + s;
        }
        return s;
    }

    /**
     * 获取给定的颜色的ARGB值
     *
     * @param color
     * @return in[4] int[0]:alpha,int[1]:red,int[2]:green,int[3]:blue
     */
    public static int[] getARGB(@ColorInt int color) {
        int a = (color >> 24) & 0xff;
        int r = (color >> 16) & 0xff;
        int g = (color >> 8) & 0xff;
        int b = (color) & 0xff;
        return new int[]{a, r, g, b};
    }

    /**
     * 根据给定的args值获取颜色
     *
     * @param argb
     * @return
     */
    public static @ColorInt
    int getColor(int[] argb) {
        if (argb == null || argb.length != 4) {
            throw new IllegalArgumentException("argb length must is 4");
        }
        return argb[0] << 24 | argb[1] << 16 | argb[2] << 8 | argb[3];
    }

    /**
     * 给color添加透明度
     *
     * @param alpha 透明度 0-255
     * @param color 原来的颜色
     * @return
     */
    public static int setColorAlpha(int alpha, @ColorInt int color) {
        int[] argb = getARGB(color);
        if (alpha < 0) {
            alpha = 0;
        } else if (alpha > 255) {
            alpha = 255;
        }
        argb[0] = alpha;
        return getColor(argb);
    }

    /**
     * 给color添加透明度
     *
     * @param alphaPercent 透明度百分比 0-1
     * @param color        原来的颜色
     * @return
     */
    public static int setColorAlphaPercent(@FloatRange(from = 0.0, to = 1.0) float alphaPercent, @ColorInt int color) {
        if (alphaPercent < 0) {
            alphaPercent = 0;
        } else if (alphaPercent > 1) {
            alphaPercent = 1;
        }
        int[] argb = getARGB(color);
        int alpha = (int) (argb[0] * alphaPercent + 0.5);
        if (alpha < 0) {
            alpha = 0;
        } else if (alpha > 255) {
            alpha = 255;
        }
        argb[0] = alpha;
        return getColor(argb);
    }

    /**
     * 设置drawable的大小
     *
     * @param drawable
     * @param width
     * @param height
     */
    public static void setDrawableSize(Drawable drawable, int width, int height) {
        if (drawable != null && (width > 0 || height > 0)) {
            Rect oldBounds = drawable.getBounds();
            int left = oldBounds.left;
            int top = oldBounds.top;
            int right = oldBounds.right;
            int bottom = oldBounds.bottom;
            if (width > 0) {
                right = left + width;
            }
            if (height > 0) {
                bottom = top + height;
            }
            drawable.setBounds(left, top, right, bottom);
        }
    }

    /**
     * 升序排序
     *
     * @param str1
     * @param str2
     * @return
     */
    public static int compareTo(String str1, String str2) {
        if (isNullOrEmpty(str1) && isNullOrEmpty(str2)) {
            return 0;
        } else if (isNullOrEmpty(str1)) {
            return 1;
        } else if (isNullOrEmpty(str2)) {
            return -1;
        } else {
            return str1.compareTo(str2);
        }
    }

    /**
     * 断给定字符串是否空白串
     * null、"null"、" "
     *
     * @param str
     * @return
     */
    public static boolean isNullOrEmpty(Object str) {
        if (str == null || "".equals(str.toString().trim()) || "null".equalsIgnoreCase(str.toString()))
            return true;
        return false;
    }

    /**
     *  DecimalFormat:
     * 1.整数：若是n个0，就从个位开始向高位填充，如果有值就是原来的值，没有就填充0。
     *                 若都是#，没有实际意义，不管是几个#，最后的结果都是原来的整数。
     *                 0和#配合使用，只能是"##00",不能是"00##",就是#在前0在后。实现是上面的合集。
     *2.小数：是可以保留小数点后几位的（几个0后或几个#）。
     *                 若n个0，就是保留n位小数，小数不足的部分用0填充。
     *                 若n个#，就是保留n位小数，小数不足部分没有就是没有。
     *                 0和#配合使用，只能是".00##",不能是".##00",就是0在前#在后。实现和上面一样
     */

    /**
     * 格式化输出字符串,保留几位小数
     *
     * @param value    小数字符串
     * @param decimals 保留几位小数
     * @param fillEnd  小数位数不够结尾是否填充0
     * @return
     */
    public static String formatDecimalUp(String value, int decimals, boolean fillEnd) {
        StringBuilder patternBuilder = new StringBuilder();
        if (decimals == 0) {
            patternBuilder.append("#");
        } else {
            patternBuilder.append("#.");
            for (int i = 0; i < decimals; i++) {
                if (fillEnd) {
                    patternBuilder.append("0");
                } else {
                    patternBuilder.append("#");
                }
            }
        }
        String pattern = patternBuilder.toString();
        if (DEBUG) {
            Logger.d(TAG, "pattern:" + pattern);
        }
        DecimalFormat decimalFormat = new DecimalFormat(pattern);
        decimalFormat.setRoundingMode(RoundingMode.HALF_UP);
        String format = decimalFormat.format(new BigDecimal(value));
        if (format != null && format.startsWith(".")) {
            format = "0" + format;
        }
        return format;
    }

    /**
     * 格式化输出字符串,保留几位小数
     *
     * @param value    小数字符串
     * @param decimals 保留几位小数
     * @param fillEnd  小数位数不够结尾是否填充0
     * @return
     */
    public static String formatDecimalUp(double value, int decimals, boolean fillEnd) {
        return formatDecimalUp(String.valueOf(value), decimals, fillEnd);
    }

    /**
     * 柱状图计算柱子的宽度
     * 每个x有多个并排的柱状图时计算每个柱子的宽度
     * 关键：(barWidth + barSpace) * size + groupSpace = 1.00 -> interval per "group" 一定要等于1,乘以size是表示每组有两个数据
     *
     * @param barNumber  每组柱子数量
     * @param groupSpace groupSpace 每组之间的距离百分比
     * @param barSpace   barSpace 每个柱子之间的距离百分比
     * @return
     */
    public static float calculateBarWidth(int barNumber, float groupSpace, float barSpace) {
        return (1 - groupSpace) / barNumber - barSpace;
    }

    /**
     * 关闭marker
     *
     * @param chart
     */
    public static void closeMarker(Chart chart) {
        try {
            //高亮
            if (chart != null && chart.valuesToHighlight()) {
                chart.highlightValue(null);
                chart.invalidate();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 字符串格式化
     * 占位符:
     * 转换符    详细说明         示例
     * %s	字符串类型	        "简书"
     * %c	字符类型	            'm'
     * %b	布尔类型        	    true
     * %d	整数类型（十进制）	88
     * %x	整数类型（十六进制）	FF
     * %o	整数类型（八进制）	77
     * %f	浮点类型	          8.888      %2f 保留2位小数
     * %a	十六进制浮点类型	    FF.35AE
     * %e	指数类型	            9.38e + 5
     * %%	百分比类型	        %（%特殊字符%%才能显示%）
     *
     * @param format 需要格式化的字符串 eg: 阅读%d次
     * @param args   占位符的值 eg:2
     * @return
     */
    public static String formatString(String format, Object... args) {
        return String.format(Locale.CANADA, format, args);
    }

    public static int getColorForIndex(int[] colors, int index) {
        if (colors != null && colors.length > 0) {
            return colors[index % colors.length];
        }
        return 0;
    }

    public static int getColorForIndex(List<Integer> colors, int index) {
        if (colors != null && colors.size() > 0) {
            return colors.get(index % colors.size());
        }
        return 0;
    }

    /**
     * 根据颜色数组创建一个指定数量的新颜色数组
     *
     * @param srcColors
     * @param count
     * @return
     */
    public static int[] createColors(int[] srcColors, int count) {
        if (count > 0 && srcColors != null && srcColors.length > 0) {
            int[] result = new int[count];
            for (int i = 0; i < count; i++) {
                result[i] = srcColors[i % srcColors.length];
            }
            return result;
        }
        return null;
    }


    public static GradientColor getGradientColorForIndex(List<GradientColor> colors, int index) {
        if (colors != null && colors.size() > 0) {
            return colors.get(index % colors.size());
        }
        return null;
    }

    public static boolean notEmpty(List<?> list) {
        return list != null && list.size() > 0;
    }

    @SuppressWarnings("all")
    public static int getDataSetIndex(CombinedData combinedData, IDataSet dataSet) {
        if (combinedData != null && dataSet != null) {
            List<IBarLineScatterCandleBubbleDataSet<? extends Entry>> dataSets = combinedData.getDataSets();
            if (dataSets != null && dataSets.size() > 0) {
                return dataSets.indexOf(dataSet);
            }
        }
        return -1;
    }

    /**
     * 限制文本最大显示长度,超出的部分显示省略号
     *
     * @param s
     * @param maxLength
     * @param where     {@link TextUtils.TruncateAt#START,TextUtils.TruncateAt#MIDDLE,TextUtils.TruncateAt#END}
     * @return
     */
    public static String ellipsize(String s, int maxLength, TextUtils.TruncateAt where) {
        if (s == null || s.length() <= maxLength || maxLength <= 0) {
            return s;
        }
        int len = Math.max(maxLength - 1, 1);
        if (where != null) {
            switch (where) {
                case START:
                    return "···" + s.substring(s.length() - len);
                case MIDDLE:
                    int start, end;
                    if (len % 2 == 0) {
                        start = len / 2;
                        end = s.length() - start;
                    } else {
                        start = len / 2 + 1;
                        end = s.length() - len / 2;
                    }
                    return s.substring(0, start) + "···" + s.substring(end);
                case END:
                    return s.substring(0, len) + "···";
            }
        }
        return s.substring(0, len) + "···";
    }

    /**
     * 限制文本最大显示长度,超出的部分开始显示省略号
     *
     * @param s
     * @param maxLength
     * @return
     */
    public static String ellipsizeStart(String s, int maxLength) {
        return ellipsize(s, maxLength, TextUtils.TruncateAt.START);
    }

    /**
     * 限制文本最大显示长度,超出的部分结尾显示省略号
     *
     * @param s
     * @param maxLength
     * @return
     */
    public static String ellipsizeEnd(String s, int maxLength) {
        return ellipsize(s, maxLength, TextUtils.TruncateAt.END);
    }

    /**
     * 限制文本最大显示长度,超出的部分中间显示省略号
     *
     * @param s
     * @param maxLength
     * @return
     */
    public static String ellipsizeMiddle(String s, int maxLength) {
        return ellipsize(s, maxLength, TextUtils.TruncateAt.MIDDLE);
    }

    /**
     * 根据数据创建图例
     *
     * @param data
     * @param shapeArray key:图表类型{@link MPConstant#BAR_CHART ...}, value:图例形状{@link MPIconView#LINE ...}
     * @return
     */
    @SuppressWarnings("all")
    public static List<MPLegendEntry> computeLegend(ChartData<? extends IDataSet<? extends Entry>> data, SparseIntArray shapeArray) {
        if (data == null || data.getDataSetCount() == 0) return null;
        List<IDataSet> dataSets = new ArrayList<>(data.getDataSets());
        //把曲线排在后面
        Collections.sort(dataSets, IDataSetComparator);
        int barIcon = shapeArray != null ? shapeArray.get(MPConstant.BAR_CHART, MPIconView.RECTANGLE) : MPIconView.RECTANGLE;
        int lineIcon = shapeArray != null ? shapeArray.get(MPConstant.LINE_CHART, MPIconView.LINE) : MPIconView.LINE;
        int pieIcon = shapeArray != null ? shapeArray.get(MPConstant.PIE_CHART, MPIconView.CIRCLE) : MPIconView.CIRCLE;
        List<MPLegendEntry> mpLegendEntries = new ArrayList<>();
        for (int i = 0; i < dataSets.size(); i++) {
            IDataSet dataSet = dataSets.get(i);
            List<Integer> clrs = dataSet.getColors();
            int entryCount = dataSet.getEntryCount();
            // if we have a barchart with stacked bars
            if (dataSet instanceof IBarDataSet && ((IBarDataSet) dataSet).isStacked()) {
                IBarDataSet bds = (IBarDataSet) dataSet;
                String[] sLabels = bds.getStackLabels();
                for (int j = 0; j < clrs.size() && j < bds.getStackSize(); j++) {
                    mpLegendEntries.add(new MPLegendEntry(
                            sLabels[j % sLabels.length],
                            barIcon,
                            clrs.get(j),
                            dataSet));
                }

            } else if (dataSet instanceof IPieDataSet) {
                IPieDataSet pds = (IPieDataSet) dataSet;
                for (int j = 0; j < clrs.size() && j < entryCount; j++) {
                    mpLegendEntries.add(new MPLegendEntry(
                            pds.getEntryForIndex(j).getLabel(),
                            pieIcon,
                            clrs.get(j),
                            dataSet));

                }
            } else if (dataSet instanceof ICandleDataSet && ((ICandleDataSet) dataSet).getDecreasingColor() !=
                    ColorTemplate.COLOR_NONE) {

                int decreasingColor = ((ICandleDataSet) dataSet).getDecreasingColor();
                int increasingColor = ((ICandleDataSet) dataSet).getIncreasingColor();

                mpLegendEntries.add(new MPLegendEntry(
                        dataSet.getLabel(),
                        MPIconView.CIRCLE,
                        increasingColor,
                        dataSet
                ));

            } else { // all others

                for (int j = 0; j < clrs.size() && j < entryCount; j++) {

                    String label;
                    MPLegendEntry mpLegendEntry = new MPLegendEntry(dataSet);
                    // if multiple colors are set for a DataSet, group them
                    if (j < clrs.size() - 1 && j < entryCount - 1) {
                        label = null;
                    } else { // add label to the last entry
                        label = dataSet.getLabel();
                    }
                    int icon = MPIconView.CIRCLE;
                    if (dataSet instanceof IBarDataSet) {
                        icon = barIcon;
                    } else if (dataSet instanceof ILineDataSet) {

                        icon = lineIcon;
                        ILineDataSet iLineDataSet = (ILineDataSet) dataSet;
                        mpLegendEntry.setCircleHoleColor(iLineDataSet.getCircleHoleColor());
                        mpLegendEntry.setCircleColor(iLineDataSet.getCircleColor(0));
                    } else if (dataSet instanceof IPieDataSet) {
                        icon = pieIcon;
                    }
                    mpLegendEntry.setLabel(label);
                    mpLegendEntry.setIconShape(icon);
                    mpLegendEntry.setIconColor(clrs.get(j));
                    mpLegendEntries.add(mpLegendEntry);
                }
            }
        }
        return mpLegendEntries;
    }

    /**
     * 把曲线排在后面
     */
    @SuppressWarnings("all")
    public static final Comparator<IDataSet> IDataSetComparator = new Comparator<IDataSet>() {
        @Override
        public int compare(IDataSet dataSet1, IDataSet dataSet2) {
            if (dataSet1 instanceof ILineDataSet && !(dataSet2 instanceof ILineDataSet)) {
                return 1;
            } else if (!(dataSet1 instanceof ILineDataSet) && dataSet2 instanceof ILineDataSet) {
                return -1;
            }
            return 0;
        }
    };

    /**
     * 从原集合中复制指定条数的数据到新集合
     *
     * @param src       原集合
     * @param copyCount 需要复制的数量
     * @param repeat    需要复制的数量大于原集合的数量时是否从头开始循环取值知道达到指定数量
     * @param <T>
     * @return
     */
    public static <T> List<T> copyList(@NonNull List<T> src, int copyCount, boolean repeat) {
        if (src.size() > 0 && copyCount > 0) {
            int srcSize = src.size();
            int count = copyCount;
            if (!repeat) {
                count = Math.min(copyCount, srcSize);
            }
            List<T> result = new ArrayList<>();
            for (int i = 0; i < count; i++) {
                result.add(src.get(i % srcSize));
            }
            return result;
        }
        return null;
    }

    /**
     * 从原集合中复制指定条数的数据到新集合
     *
     * @param src       原集合
     * @param copyCount 需要复制的数量
     * @param <T>
     * @return
     */
    public static <T> List<T> copyList(@NonNull List<T> src, int copyCount) {
        return copyList(src, copyCount, false);
    }


    /**
     * 将dip或dp值转换为px值，保证尺寸大小不变
     *
     * @param dipValue （DisplayMetrics类中属性density）
     * @return
     */
    public static int dpTopx(Context context,float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }




}
